package com.dsdl.chatapp.common.utils.socket;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.util.Log;

import com.dsdl.chatapp.contacts.Constant;
import com.dsdl.chatapp.data.SocketMessage;
import com.dsdl.chatapp.data.VerifyMessage;
import com.dsdl.chatapp.common.utils.AESUtil;
import com.dsdl.chatapp.common.utils.LogUtil;
import com.dsdl.chatapp.common.utils.SPUtil;
import com.dsdl.chatapp.common.utils.StringUtils;
import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by zzg on 2016/8/2.
 * NewSocketManager
 */
public class NewSocketManager implements ISocketManager {
    private static String TAG = NewSocketManager.class.getSimpleName();
    private static String IP = "192.168.0.123";
    private static int PORT = 3101;
    private static int TIME_OUT = 15000;
    /**
     * SocketManager对象
     */
    private static NewSocketManager mSocketManager;
    /**
     * Socket通信对象
     */
    private Socket mSocket;
    /**
     * UI更新通知handler
     */
    private UIHandler mUIHandler;
    /**
     * 接收消息监听
     */
    private ReceiveListener mReceiveListener;
    /**
     * 发送消息监听
     */
    private SendCallBack mSendCallBack;
    /**
     * 当前连接状态
     */
    private State state;

    private SendState mSendState = SendState.END;

    private SendThread mSendThread;
    private ReceiveThread mReceiveThread;

    private NewSocketManager() {
        mSocket = new Socket();
    }

    public synchronized static NewSocketManager getInstance() {
        if (mSocketManager == null) {
            mSocketManager = new NewSocketManager();
        }
        return mSocketManager;
    }

    private Socket getRunningSocket() {
        if (this.mSocket == null) {
            this.mSocket = new Socket();
        }
        return this.mSocket;
    }

    /**
     * 获取UIHandler对象
     *
     * @return handler对象
     */
    private UIHandler getUIHandler() {
        if (mUIHandler == null) {
            mUIHandler = new UIHandler(this);
        }
        return mUIHandler;
    }

    /**
     * 消息发送状态
     */
    public enum SendState {
        BEGIN, SENDING, END
    }

    /**
     * 设置发送状态
     *
     * @param sendState
     * @return
     */
//    private SocketManager setSendState(SendState sendState) {
//        this.mSendState = sendState;
//        return this;
//    }

    /**
     * 获取发送状态
     *
     * @return
     */
//    private SendState getSendState() {
//        if (this.mSendState == null) {
//            this.mSendState = SendState.END;
//        }
//        return this.mSendState;
//    }
    @Override
    public void connect(ReceiveListener receiveListener) {
        if (!(getState() == State.DISCONNECTED)) {
            LogUtil.d("zzg", "already connected");
            return;
        }
        mReceiveListener = receiveListener;
        setState(State.CONNECTING);
        getConnectThread().start();

    }

    @Override
    public void sendMessage(SocketPacket socketPacket, SendCallBack callBack) {
        this.mSendCallBack = callBack;

//        if (getSendState() != SendState.END) {//只有发送状态是END时才能发送文本消息
//            this.mSendCallBack.error("发送状态错误");
//            return;
//        }
        if (socketPacket != null && socketPacket.getData() != null) {
//            setSendState(SendState.BEGIN);
            getSendThread().enqueueSocketPacket(socketPacket);
        } else {
            if (mSendCallBack != null) {
                mSendCallBack.error("发送的消息体不能为null");
            }
        }

    }

    public void disconnect() {
        if (state == State.DISCONNECTED) {
            return;
        }
        if (!getRunningSocket().isClosed() || getState() == State.CONNECTING) {
            try {
                getRunningSocket().getOutputStream().close();
                getRunningSocket().getInputStream().close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    getRunningSocket().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                this.mSocket = null;
            }
        }
        if (this.mShakeHandsThread != null) {
            this.mShakeHandsThread.interrupt();
            this.mShakeHandsThread = null;
        }
        if (this.mSendThread != null) {
            this.mSendThread.interrupt();
            this.mSendThread = null;
        }
        if (this.mReceiveThread != null) {
            this.mReceiveThread.interrupt();
            this.mReceiveThread = null;
        }
        getUIHandler().sendEmptyMessage(MessageType.DISCONNECTED.what());
    }

    private ShakeHandsThread mShakeHandsThread;

    private ShakeHandsThread getConnectThread() {
        if (this.mShakeHandsThread == null) {
            this.mShakeHandsThread = new ShakeHandsThread();
        }
        return this.mShakeHandsThread;
    }

    protected SendThread getSendThread() {
        if (this.mSendThread == null) {
            this.mSendThread = new SendThread();
        }
        return this.mSendThread;
    }

    protected ReceiveThread getReceiveThread() {
        if (this.mReceiveThread == null) {
            this.mReceiveThread = new ReceiveThread();
        }
        return this.mReceiveThread;
    }

    /**
     * 设置连接状态
     *
     * @param state
     * @return
     */
    protected NewSocketManager setState(State state) {
        this.state = state;
        return this;
    }

    /**
     * 获取连接状态
     *
     * @return
     */
    public State getState() {
        if (this.state == null) {
            return State.DISCONNECTED;
        }
        return this.state;
    }

    /**
     * 是否是连接成功的状态
     *
     * @return
     */
    private boolean isConnected() {
        return getState() == State.CONNECTED;
    }

    /**
     * 是否是握手连接状态
     * @return
     */
    private boolean isConnecting(){
        return getState() == State.CONNECTING;
    }

    private boolean internalCheckShouldContinueSend(SocketPacket packet) {
        if (packet.isCanceled() || Thread.interrupted()) {
            internalCancelSendPacket(packet);
            return false;
        }
        return true;
    }

    @UiThread
    @CallSuper
    protected void internalOnConnected() {
        setState(State.CONNECTED);
        getSendThread().start();
        getReceiveThread().start();
    }

    @UiThread
    @CallSuper
    protected void internalOnDisconnected() {
        setState(State.DISCONNECTED);
//        disconnect();
    }

    @UiThread
    @CallSuper
    protected void internalOnReceiveResponse(@NonNull String responsePacket) {
        mReceiveListener.receiverSuccess(responsePacket);
    }

    @UiThread
    @CallSuper
    protected void internalOnSendPacketBegin() {
        if (mSendCallBack != null) {
            mSendCallBack.begin();
        }
    }

    @UiThread
    @CallSuper
    private void internalCancelSendPacket(SocketPacket packet) {
        Message message = Message.obtain();
        message.what = MessageType.SENDING_CANCEL.what();
        message.obj = packet;
        mSocketManager.getUIHandler().sendMessage(message);
    }

    @UiThread
    @CallSuper
    private void internalEndSendPacket(SocketPacket packet) {
        Message message = Message.obtain();
        message.what = MessageType.SENDING_END.what();
        message.obj = packet;
        mSocketManager.getUIHandler().sendMessage(message);
    }

    /**
     * 发送验证消息
     */
    private SocketPacket sendVerifyMessage(){
        if (!StringUtils.isEmpty(SPUtil.getSharedStringData(Constant.USER_PHONE,null)) && !StringUtils.isEmpty(SPUtil.getSharedStringData(Constant.USER_TOKEN,null))){
            SocketPacket packet = new SocketPacket();
            VerifyMessage verifyMessage = new VerifyMessage();
            verifyMessage.setPhone(SPUtil.getSharedStringData(Constant.USER_PHONE));
            verifyMessage.setToken(SPUtil.getSharedStringData(Constant.USER_TOKEN));
            packet.setCommand(new Gson().toJson(verifyMessage));
            return packet;
        }
        return null;
    }

    /**
     * Socket登录
     */
    class ShakeHandsThread extends Thread{
        public ShakeHandsThread(){}
        @Override
        public void run() {
            super.run();
            try {
                mSocketManager.getRunningSocket().connect(new InetSocketAddress(IP, PORT), TIME_OUT);//第一次握手
                mSocketManager.getUIHandler().sendEmptyMessage(MessageType.CONNECT.what());
                //第二次握手
                SocketPacket socketPacket = sendVerifyMessage();
                if (socketPacket != null && socketPacket.getCommand() != null){
                    if (mSocketManager.getRunningSocket().isClosed()) {
                        LogUtil.d(TAG, "socket is closed");
                    }
                    String encodeMessage = AESUtil.encode(socketPacket.getCommand());
                    mSocketManager.getRunningSocket().getOutputStream().write((encodeMessage).getBytes());
                    mSocketManager.getRunningSocket().getOutputStream().flush();
                }
//                //第三次握手
//                InputStream inputStream = mSocketManager.getRunningSocket().getInputStream();
//                InputStreamReader streamReader = new InputStreamReader(inputStream);
//                //链接数据串流，建立BufferedReader来读取，将BufferReader链接到InputStreamReader
//                BufferedReader reader = new BufferedReader(streamReader);
//                if (mSocketManager.isConnecting() && !Thread.interrupted()) {
//                    char chars[] = new char[1024];
//                    int len;
//                    while ((len=reader.read(chars)) != -1) {
//                        String readStr = new String(chars,0,len);
//                        mSocketManager.getUIHandler().sendEmptyMessage(MessageType.CONNECT.what());
//                        LogUtil.d(TAG,readStr);
//                        break;
//                    }
//                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 发送消息线程
     */
    class SendThread extends Thread {
        public SendThread() {
        }

        private LinkedBlockingQueue<SocketPacket> sendingPacketQueue;
        private SocketPacket sendingSocketPacket;

        //发送消息队列
        protected LinkedBlockingQueue<SocketPacket> getSendingPacketQueue() {
            if (sendingPacketQueue == null) {
                sendingPacketQueue = new LinkedBlockingQueue<SocketPacket>();
            }
            return sendingPacketQueue;
        }

        protected SendThread setSendingSocketPacket(SocketPacket sendingSocketPacket) {
            this.sendingSocketPacket = sendingSocketPacket;
            return this;
        }

        public SocketPacket getSendingSocketPacket() {
            return this.sendingSocketPacket;
        }

        public void enqueueSocketPacket(final SocketPacket socketPacket) {
            if (getSendingSocketPacket() == socketPacket || getSendingPacketQueue().contains(socketPacket)) {
                Log.w(TAG, "消息队列中已经存在此消息");
//                setSendState(SendState.END);
                return;
            }
            try {
                getSendingPacketQueue().put(socketPacket);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            super.run();
            SocketPacket packet;
            try {
                while (mSocketManager.isConnected() && !Thread.interrupted() && (packet = getSendingPacketQueue().take()) != null) {
                    setSendingSocketPacket(packet);
                    packet.setCanceled(false);
                    if (!internalCheckShouldContinueSend(packet)) {
                        continue;
                    }
                    SocketMessage msg = packet.getData();
                    if (msg != null) {
                        if (mSocketManager.getRunningSocket().isClosed()) {
                            LogUtil.d(TAG, "socket is closed");
                        }
                        String json = new Gson().toJson(msg);
                        String encodeJson = AESUtil.encode(json);
                        try {
                            mSocketManager.getRunningSocket().getOutputStream().write(encodeJson.getBytes());
                            mSocketManager.getRunningSocket().getOutputStream().flush();
                            internalEndSendPacket(packet);
                        } catch (IOException e) {
                            e.printStackTrace();
                            if (mSocketManager.getRunningSocket().isClosed()) {
                                LogUtil.d(TAG, "发送消息异常");
                            }
//                            setSendState(SendState.END);
                        }
                    }

                }
            } catch (InterruptedException e) {
                e.printStackTrace();
//                setSendState(SendState.END);
            }
        }
    }

    /**
     * 接收消息线程
     */
    private class ReceiveThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                if (mSocketManager.getRunningSocket().isClosed()) {
                    LogUtil.d("zzg", "socket is closed");
                }
                InputStream inputStream = mSocketManager.getRunningSocket().getInputStream();
                InputStreamReader streamReader = new InputStreamReader(inputStream);
                //链接数据串流，建立BufferedReader来读取，将BufferReader链接到InputStreamReader
                BufferedReader reader = new BufferedReader(streamReader);
                if ((mSocketManager.isConnected() || mSocketManager.isConnecting()) && !Thread.interrupted()){
//                    String advice = null;
//                    while ((advice = reader.readLine()) != null && advice.length()!=0) {
//                        LogUtil.d(TAG,advice);
//                        Message message = Message.obtain();
//                        message.what = MessageType.RECEIVE_RESPONSE.what();
//                        message.obj = advice;
//                        mSocketManager.getUIHandler().sendMessage(message);
//                    }
                    char chars[] = new char[1024];
                    int len;
                    while ((len=reader.read(chars)) != -1) {
                        String readString = new String(chars, 0, len);
                        Message message = Message.obtain();
                        message.what = MessageType.RECEIVE_RESPONSE.what();
                        message.obj = readString;
                        mSocketManager.getUIHandler().sendMessage(message);
                    }
                    inputStream.close();
                    streamReader.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }

        }
    }

    class UIHandler extends Handler {
        //弱引用
        private WeakReference<NewSocketManager> referenceSocketManager;

        public UIHandler(@NonNull NewSocketManager referenceSocketClient) {
            super(Looper.getMainLooper());
            this.referenceSocketManager = new WeakReference<>(referenceSocketClient);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (this.referenceSocketManager.get() == null) {
                return;
            }
            switch (MessageType.typeFromWhat(msg.what)) {
                case CONNECT:
                    this.referenceSocketManager.get().internalOnConnected();
                    break;
                case CONNECT_TIME_OUT:
                    break;
                case DISCONNECTED:
                    this.referenceSocketManager.get().internalOnDisconnected();
                    break;
                case RECEIVE_RESPONSE:
                    this.referenceSocketManager.get().internalOnReceiveResponse((String) msg.obj);
                    break;
                case SENDING_BEGIN:
                    this.referenceSocketManager.get().internalOnSendPacketBegin();
                    break;
                case SENDING_CANCEL:
                    break;
                case SENDING_END:
                    if (msg.obj instanceof SocketPacket) {
                        if (mSendCallBack != null) {
                            mSendCallBack.success((SocketPacket) msg.obj);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }
}
